float4x4 World;
float4x4 View;
float4x4 Projection;
float jelloAlpha;
float maxJelloAlpha;
float minJelloAlpha;

float3 cameraPosition;
float2 fInverseViewportDimensions;

#define maxPointLights 8
#define maxDirectionalLights 3

int numPointLights;
float3 xPointLightPositions[maxPointLights];
float xPointLightIntensities[maxPointLights];
float4 xPointLightColors[maxPointLights];

int numDirectionalLights;
float3 xDirectionalLightDirections[maxDirectionalLights];
float xDirectionalLightIntensities[maxDirectionalLights];
float4 xDirectionalLightColors[maxDirectionalLights];
float4x4 xDirectionalLightWorlds[maxDirectionalLights];
float4x4 xDirectionalLightViews[maxDirectionalLights];
float4x4 xDirectionalLightProjections[maxDirectionalLights];

float4 xAmbientColor;
float xAmbientIntensity;

texture2D shadowDepthMapTexture0;
texture2D shadowDepthMapTexture1;
texture2D shadowDepthMapTexture2;
sampler2D shadowDepthSampler0 = sampler_state
{
	Texture = <shadowDepthMapTexture0>;
	MinFilter = point;
	MagFilter = point;
	MipFilter = point;
};
sampler2D shadowDepthSampler1 = sampler_state
{
	Texture = <shadowDepthMapTexture1>;
	MinFilter = point;
	MagFilter = point;
	MipFilter = point;
};
sampler2D shadowDepthSampler2 = sampler_state
{
	Texture = <shadowDepthMapTexture2>;
	MinFilter = point;
	MagFilter = point;
	MipFilter = point;
};

texture2D jelloDepthMap;
sampler2D depthSampler = sampler_state
{
	Texture = <jelloDepthMap>;
	MinFilter = point;
	MagFilter = point;
	MipFilter = point;
};

texture2D jelloBumpTexture;
sampler2D jelloBumpSampler = sampler_state
{
	Texture = <jelloBumpTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	AddressU = Wrap;
	AddressV = Wrap;
};

struct JelloVertexShaderInput
{
    float4 Position : POSITION0;
	float4 Color : COLOR0;
	float3 Normal : NORMAL0;
	float2 Texcoord : TEXCOORD0;
	float3 Binormal : BINORMAL0;
	float3 Tangent : TANGENT0;
};

struct JelloVertexShaderOutput
{
    float4 Position : POSITION0;
    float4 Position3D : TEXCOORD0;
	float CamDistance : TEXCOORD1;
    float3 Normal : TEXCOORD2;
	float2 Texcoord : TEXCOORD3;
	float4 Color : COLOR0;
	float3x3 WorldToTangentSpace : TEXCOORD4;
};

struct FlatVertexShaderInput
{
    float4 Position : POSITION0;
	float4 Color : COLOR0;
	float3 Normal : NORMAL0;
};

struct FlatVertexShaderOutput
{
    float4 Position : POSITION0;
    float4 Position3D : TEXCOORD0;
	float CamDistance : TEXCOORD1;
    float3 Normal : TEXCOORD2;
	float4 ShadowMapPosition2D0 : TEXCOORD3;
	float4 ShadowMapPosition2D1 : TEXCOORD4;
	float4 ShadowMapPosition2D2 : TEXCOORD5;
	float4 Color : COLOR0;
};

float DotProduct(float3 lightPos, float3 pos3D, float3 normal)
{
    float3 lightDir = normalize(pos3D - lightPos);
    return dot(-lightDir, normal);    
}

JelloVertexShaderOutput JelloVertexShaderFunction(JelloVertexShaderInput input)
{
    JelloVertexShaderOutput output = (JelloVertexShaderOutput)0;

    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);

    output.Position = mul(viewPosition, Projection);
	output.Position3D =  mul(input.Position, World);
	output.Color = input.Color;
	output.Normal = normalize(mul(input.Normal, (float3x3)World));   
    output.CamDistance = distance(cameraPosition, worldPosition);
	output.Texcoord = input.Texcoord;

	output.WorldToTangentSpace[0] = mul(normalize(input.Tangent), World);
	output.WorldToTangentSpace[1] = mul(normalize(input.Binormal), World);
	output.WorldToTangentSpace[2] = mul(normalize(input.Normal), World);

    return output;
}

float4 JelloPixelShaderFunction(JelloVertexShaderOutput input, float2 screenSpace : VPOS ) : COLOR0
{
	float4 output = (float4)0;
	int i = 0;
	//Calculate normal
	float3 normalMap = 2.0 *(tex2D(jelloBumpSampler, input.Texcoord)) - 1.0;
	float3 normal = normalize(mul(normalMap, input.WorldToTangentSpace));

	//Calculate other things for lighting
	float3 pixelPosToCam = cameraPosition - input.Position3D.xyz;
	float3 di = normalize(pixelPosToCam);
	float3 dn = normal;
	
	//Diffuse point lighting
	float diffuseLightingFactor[maxPointLights];
	for (i = 0 ; i < numPointLights; i++)
	{
		diffuseLightingFactor[i] = DotProduct(xPointLightPositions[i], input.Position3D, normal);
		diffuseLightingFactor[i] = saturate(diffuseLightingFactor[i]);
	}

	//Specular point
	//Direction 3d point -> reflected ray
	float3 specularDirections[maxPointLights];
	float specularPowers[maxPointLights];
	for (i = 0 ; i < numPointLights; i++)
	{
		specularDirections[i] = normalize(2*(dot(dn, di))*dn-di);
		specularPowers[i] =  saturate(dot(di, specularDirections[i]));
		specularPowers[i] = pow(specularPowers[i], 64);
	}
	
	//Diffuse Directional 1
	float diffuseDirectionalInstensities[maxDirectionalLights];
	for (i = 0 ; i < numDirectionalLights; i++)
	{
		diffuseDirectionalInstensities[i] = dot(-normalize(xDirectionalLightDirections[i]), normal);
	}

	//Specular Directional 1
	//Direction 3d point -> reflected ray
	float specularPowerDirectional[maxDirectionalLights];
	for (i = 0 ; i < numDirectionalLights; i++)
	{
		specularPowerDirectional[i] =  saturate(dot(di, -normalize(xDirectionalLightDirections[i])));
		specularPowerDirectional[i] = pow(specularPowerDirectional[i] , 64);
	}

	//Cube alpha
	float maxdepth = tex2D(depthSampler, screenSpace*fInverseViewportDimensions + fInverseViewportDimensions*0.5).r;
	float thisdepth = input.CamDistance;
	float diff = maxdepth - thisdepth;
	float alpha = 1-pow((1-jelloAlpha), diff);
	output.a = alpha + minJelloAlpha;
	output.a = min(output.a, maxJelloAlpha);

	output.rgb = input.Color.rgb * 
		(xAmbientColor * xAmbientIntensity);
		
	for (i = 0 ; i < numDirectionalLights; i++)
	{
		output.rgb = output.rgb + input.Color.rgb *
		(diffuseDirectionalInstensities[i] * xDirectionalLightIntensities[i] * xDirectionalLightColors[i]
			+ specularPowerDirectional[i]  * xDirectionalLightIntensities[i] * xDirectionalLightColors[i]);
	}

	for (i = 0; i < numPointLights; i++)
	{
		float camDist = distance(xPointLightPositions[i], input.Position3D);
		float distanceIntensity = 1 / (camDist * camDist);
		output.rgb = output.rgb + input.Color.rgb *
			(diffuseLightingFactor[i] * xPointLightColors[i] * distanceIntensity * xPointLightIntensities[i]
			+ specularPowers[i] * xPointLightColors[i] * distanceIntensity * xPointLightIntensities[i]);
	}
	
	//output.rgba =1;
	//output = tex2D(jelloBumpSampler, input.Texcoord);
	return output;
}
FlatVertexShaderOutput FlatVertexShaderFunction(FlatVertexShaderInput input)
{
    FlatVertexShaderOutput output = (FlatVertexShaderOutput)0;
    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);

    output.Position = mul(viewPosition, Projection);
	output.Position3D =  mul(input.Position, World);
	output.Color = input.Color;
	output.Normal = normalize(mul(input.Normal, (float3x3)World));   
    output.CamDistance = distance(cameraPosition, worldPosition);
    float4 shadowWorldPosition0 = mul(input.Position, xDirectionalLightWorlds[0]);
    float4 shadowViewPosition0 = mul(shadowWorldPosition0, xDirectionalLightViews[0]);
	output.ShadowMapPosition2D0= mul(shadowViewPosition0, xDirectionalLightProjections[0]);
    float4 shadowWorldPosition1 = mul(input.Position, xDirectionalLightWorlds[1]);
    float4 shadowViewPosition1 = mul(shadowWorldPosition1, xDirectionalLightViews[1]);
	output.ShadowMapPosition2D1= mul(shadowViewPosition1, xDirectionalLightProjections[1]);
    float4 shadowWorldPosition2 = mul(input.Position, xDirectionalLightWorlds[2]);
    float4 shadowViewPosition2 = mul(shadowWorldPosition2, xDirectionalLightViews[2]);
	output.ShadowMapPosition2D2= mul(shadowViewPosition2, xDirectionalLightProjections[2]);
    return output;
}

float4 FlatPixelShaderFunction(FlatVertexShaderOutput input, float2 screenSpace : VPOS ) : COLOR0
{
	float4 output = (float4)0;
	int i = 0;

	//Calculate normal
	float3 normal = normalize(input.Normal);
	
	//Calculate other things for lighting
	float3 pixelPosToCam = cameraPosition - input.Position3D.xyz;
	float3 di = normalize(pixelPosToCam);
	float3 dn = normal;
	
	//Diffuse point lighting
	float diffuseLightingFactor[maxPointLights];
	for (i = 0 ; i < numPointLights; i++)
	{
		diffuseLightingFactor[i] = DotProduct(xPointLightPositions[i], input.Position3D, normal);
		diffuseLightingFactor[i] = saturate(diffuseLightingFactor[i]);
	}

	//Specular point
	//Direction 3d point -> reflected ray
	float3 specularDirections[maxPointLights];
	float specularPowers[maxPointLights];
	for (i = 0 ; i < numPointLights; i++)
	{
		specularDirections[i] = normalize(2*(dot(dn, di))*dn-di);
		specularPowers[i] =  saturate(dot(di, specularDirections[i]));
		specularPowers[i] = pow(specularPowers[i], 64);
	}
	
	//Diffuse Directional 1
	float diffuseDirectionalInstensities[maxDirectionalLights];
	for (i = 0 ; i < numDirectionalLights; i++)
	{
		diffuseDirectionalInstensities[i] = dot(-normalize(xDirectionalLightDirections[i]), normal);
	}

	//Specular Directional 1
	//Direction 3d point -> reflected ray
	float specularPowerDirectional[maxDirectionalLights];
	for (i = 0 ; i < numDirectionalLights; i++)
	{
		specularPowerDirectional[i] =  saturate(dot(di, -normalize(xDirectionalLightDirections[i])));
		specularPowerDirectional[i] = pow(specularPowerDirectional[i] , 64);
	}

	output.a = 1;
	output.rgb = input.Color.rgb * 
		(xAmbientColor * xAmbientIntensity);
	
	if (numDirectionalLights > 0)
	{
		float thisDistanceToLight = (input.ShadowMapPosition2D0.z/input.ShadowMapPosition2D0.w);
		float2 shadowMapTexCoords;
		shadowMapTexCoords.x = input.ShadowMapPosition2D0.x/input.ShadowMapPosition2D0.w/2.0f +0.5f; 
		shadowMapTexCoords.y = -input.ShadowMapPosition2D0.y/input.ShadowMapPosition2D0.w/2.0f +0.5f; 
		float shadowDistanceToLight = 1-tex2D(shadowDepthSampler0, shadowMapTexCoords);
		if (shadowDistanceToLight > thisDistanceToLight || !(shadowMapTexCoords.x > 0 && shadowMapTexCoords.x < 1
			&& shadowMapTexCoords.y > 0 && shadowMapTexCoords.y < 1))
		{
			output.rgb = output.rgb + input.Color.rgb *
			(diffuseDirectionalInstensities[0] * xDirectionalLightIntensities[0] * xDirectionalLightColors[0]
				+ specularPowerDirectional[0]  * xDirectionalLightIntensities[0] * xDirectionalLightColors[0]);
		}
	}
	if (numDirectionalLights > 1)
	{
		float thisDistanceToLight = (input.ShadowMapPosition2D1.z/input.ShadowMapPosition2D1.w);
		float2 shadowMapTexCoords;
		shadowMapTexCoords.x = input.ShadowMapPosition2D1.x/input.ShadowMapPosition2D1.w/2.0f +0.5f; 
		shadowMapTexCoords.y = -input.ShadowMapPosition2D1.y/input.ShadowMapPosition2D1.w/2.0f +0.5f; 
		float shadowDistanceToLight = 1-tex2D(shadowDepthSampler1, shadowMapTexCoords);
		if (shadowDistanceToLight > thisDistanceToLight || !(shadowMapTexCoords.x > 0 && shadowMapTexCoords.x < 1
			&& shadowMapTexCoords.y > 0 && shadowMapTexCoords.y < 1))
		{
			output.rgb = output.rgb + input.Color.rgb *
			(diffuseDirectionalInstensities[1] * xDirectionalLightIntensities[1] * xDirectionalLightColors[1]
				+ specularPowerDirectional[1]  * xDirectionalLightIntensities[1] * xDirectionalLightColors[1]);
		}
	}
	if (numDirectionalLights > 2)
	{
		float thisDistanceToLight = (input.ShadowMapPosition2D2.z/input.ShadowMapPosition2D2.w);
		float2 shadowMapTexCoords;
		shadowMapTexCoords.x = input.ShadowMapPosition2D2.x/input.ShadowMapPosition2D2.w/2.0f +0.5f; 
		shadowMapTexCoords.y = -input.ShadowMapPosition2D2.y/input.ShadowMapPosition2D2.w/2.0f +0.5f; 
		float shadowDistanceToLight = 1-tex2D(shadowDepthSampler2, shadowMapTexCoords);
		if (shadowDistanceToLight > thisDistanceToLight || !(shadowMapTexCoords.x > 0 && shadowMapTexCoords.x < 1
			&& shadowMapTexCoords.y > 0 && shadowMapTexCoords.y < 1))
		{
			output.rgb = output.rgb + input.Color.rgb *
			(diffuseDirectionalInstensities[2] * xDirectionalLightIntensities[2] * xDirectionalLightColors[2]
				+ specularPowerDirectional[2]  * xDirectionalLightIntensities[2] * xDirectionalLightColors[2]);
		}
	}
	for (i = 0; i < numPointLights; i++)
	{
		float camDist = distance(xPointLightPositions[i], input.Position3D);
		float distanceIntensity = 1 / (camDist * camDist);
		output.rgb = output.rgb + input.Color.rgb *
			(diffuseLightingFactor[i] * xPointLightColors[i] * distanceIntensity * xPointLightIntensities[i]
			+ specularPowers[i] * xPointLightColors[i] * distanceIntensity * xPointLightIntensities[i]);
	}

	return output;
}

technique jello
{
    pass Pass1
    {	
        VertexShader = compile vs_3_0 JelloVertexShaderFunction();
        PixelShader = compile ps_3_0 JelloPixelShaderFunction();
    }
}

technique flat
{
	pass Pass1
	{
		VertexShader = compile vs_3_0 FlatVertexShaderFunction();
		PixelShader = compile ps_3_0 FlatPixelShaderFunction();
	}
}